using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Data;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace PBR
{
    /// <summary>
    /// The main viewer control
    /// </summary>
    public partial class ViewerControl : UserControl
    {
        /// <summary>
        /// Which tool are we using
        ///     Select - click near a vector to select it
        ///     Add - click anywhere to add a vector
        ///     Remove - not currently implemented
        /// </summary>
        private enum VectorTool { Select, Add, Remove };

        #region Static Variables

        /// <summary>
        /// The distance between vectors
        /// </summary>
        private const int DISTANCE = 15;

        /// <summary>
        /// The width of the vectors brush
        /// </summary>
        private const float LINEWIDTH = 1.0f;

        /// <summary>
        /// The length of the vectors
        /// </summary>
        private const float LINELENGTH = 3.0f;
        
        /// <summary>
        /// The radius of the outer circle of user specified vectors
        /// </summary>
        private const float OUTERCIRCLERADIUS = 4.0f;

        /// <summary>
        /// The radius of the inner circle of user specifed vectors
        /// </summary>
        private const float INNERCIRCLERADIUS = 2.0f;

        /// <summary>
        /// How long b/f we do a ComputeVectors
        /// </summary>
        private const int IDLETIMEOUT = 1000;
        
        /// <summary>
        /// Length of user created vectors
        /// </summary>
        private const int VECTORLENGTH = 15;

        #endregion

        #region Member Variables

        private Scene scene;
        private MainForm mainForm;

        private int distance = DISTANCE;
        private float lineWidth = LINEWIDTH;
        private float lineLength = LINELENGTH;
        private float outerCircleRadius = OUTERCIRCLERADIUS;
        private float innerCircleRadius = INNERCIRCLERADIUS;
        private Pen unselectedPen = new Pen(Color.Cyan, 1.0f);
        private Pen selectedPen = new Pen(Color.GreenYellow, 2.0f);
        private Pen vectorPen = new Pen(Color.YellowGreen, 1.0f);
        private VectorTool vectorTool = VectorTool.Add;
        private byte[,] vectorGrid;
        private int vectorGridDim;

        /// <summary>
        /// Do we need to do a ComputeVectors
        /// </summary>
        private bool vectorsDirty = false;

        /// <summary>
        /// How long have we been idle
        /// </summary>
        private int idleTime = 0;

        // do we show the segment borders in the paint view
        private bool showBorders = false;

        private Bitmap segmentsDisplayImage;

        // Interaction Elements
        private Point mouseDownPt;
        private bool isDraggingAdd = false;
        private bool isDraggingMove = false;

        #endregion 

        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="scene">The working scene</param>
        /// <param name="mainForm">The main form</param>
        public ViewerControl(Scene scene, MainForm mainForm)
        {
            InitializeComponent();

            this.scene = scene;
            this.mainForm = mainForm;

            vectorGridDim = scene.Painting.ActiveLayer.ColorImage.Width / distance + 1;
            vectorGrid = new byte[vectorGridDim, vectorGridDim];

            segmentsDisplayImage = scene.Painting.ActiveLayer.SegmentsImage.Bitmap;

            // Register mouse wheel functions
            this.colorBkgndPanel.MouseWheel += handle_zoom;
            this.colorPanel.MouseWheel += handle_zoom;
            MouseWheel += new MouseEventHandler(handle_zoom);

            // activate the idle timer
            idleTimer.Enabled = true;

        }

        #endregion

        #region Color Panel

        private void colorPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Bitmap image = scene.Painting.ActiveLayer.ColorImage.Bitmap;
            float zoom = scene.Zoom;
            Size zoomSize = new Size((int)(image.Width * zoom), (int)(image.Height * zoom));
            Point topPt = new Point((int)((g.VisibleClipBounds.Width - zoomSize.Width) / 2),
                 (int)((g.VisibleClipBounds.Height - zoomSize.Height) / 2));
            Point centerTopRight = new Point(
                (int)((g.VisibleClipBounds.Width - image.Width) / 2),
                (int)((g.VisibleClipBounds.Height - image.Height) / 2));
            Rectangle imageRect = new Rectangle(centerTopRight, image.Size);

            g.FillRectangle(new SolidBrush(Color.LightGray), g.ClipBounds);
            g.DrawImage(image, imageRect);
        }

        /// <summary>
        /// Start a new project - prompts the user to load a new color image
        /// and a new edges image
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void openToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            // get the color image
            openFileDialog1.Title = "Choose a Color Image";
            openFileDialog1.Filter = "PNG(.png)|*.png";
            string colorPath;
            if (openFileDialog1.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            colorPath = openFileDialog1.FileName;
            
            // get the edges image
            openFileDialog1.Title = "Choose a Segment/Edges Source Image";
            openFileDialog1.Filter = "PNG(.png)|*.png";
            string edgesPath;
            if (openFileDialog1.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            edgesPath = openFileDialog1.FileName;

            // open the color iamge
            scene.Painting.ActiveLayer.ColorImage.Dispose();
            scene.Painting.ActiveLayer.ColorImage = new BitmapUnsafe(colorPath);

            // open the image
            scene.Painting.ActiveLayer.SegmentsImage.Dispose();
            //AForge.Imaging.Filters.GrayscaleBT709 grayscaleFilter = new AForge.Imaging.Filters.GrayscaleBT709();
            scene.Painting.ActiveLayer.SegmentsImage.Bitmap = new Bitmap(edgesPath);

            // create a new image tier - contains the blurred color and edges images
            scene.Painting.ActiveLayer.ImageTier.Dispose();
            scene.Painting.ActiveLayer.ImageTier = new Tier(scene.Painting.ActiveLayer.ColorImage.Bitmap, scene.Painting.ActiveLayer.SegmentsImage.Bitmap);
            scene.Painting.ActiveLayer.PaintImage.Dispose();
            scene.Painting.ActiveLayer.PaintImage = new BitmapUnsafe("PaintImage", scene.Painting.ActiveLayer.ColorImage);

            // segment the image
            scene.Renderer.Painter.SegmentImage();

            // create a new vector image
            scene.Painting.ActiveLayer.VectorImage.Dispose();
            scene.Painting.ActiveLayer.VectorImage = new BitmapUnsafe("VectorImage", scene.Painting.ActiveLayer.ColorImage);

            // clear
            //scene.Painting.ActiveLayer.ObjectLabels = new int[scene.Painting.ActiveLayer.ColorImage.Width, scene.Painting.ActiveLayer.ColorImage.Height];
            scene.Painting.ActiveLayer.StrokeArray = new Stroke[scene.Painting.ActiveLayer.ColorImage.Width, scene.Painting.ActiveLayer.ColorImage.Height];
            
            // update the UI components
            mainForm.NewImage();

            // change the sizes of the windows to fit the new image
            UpdateSizes();

            // compute the new vector field/brush strokes
            ComputeVectorField();

            // refresh all open windows
            mainForm.Refresh();
        }

        private void colorPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Add:
                    scene.Zoom += 0.25f;
                    vectorPanel.Invalidate();
                    break;
                case Keys.Subtract:
                    scene.Zoom -= 0.25f;
                    vectorPanel.Invalidate();
                    break;
                default:
                    break;
            }
        }

        private void saveTierToolStripMenuItem_Click(object sender, EventArgs e)
        {
            int i = 0;
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.ColorImages)
            {
                bU.Bitmap.Save(Path.Combine(scene.BasePath, "colorB" + i.ToString() + ".png"));
                i++;
            }
            i = 0;
            foreach (BitmapUnsafe bU in scene.Painting.ActiveLayer.ImageTier.EdgesImages)
            {
                bU.Bitmap.Save(Path.Combine(scene.BasePath, "edges" + i.ToString() + ".png"));
                i++;
            }
        }

        #endregion

        #region Specular Panel

        private void specularPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Bitmap image = scene.Painting.ActiveLayer.SpecularImage.Bitmap;
            Point centerTopRight = new Point(
                (int)((g.VisibleClipBounds.Width - image.Width) / 2),
                (int)((g.VisibleClipBounds.Height - image.Height) / 2));
            Rectangle imageRect = new Rectangle(centerTopRight, image.Size);

            g.FillRectangle(new SolidBrush(Color.LightGray), g.ClipBounds);
            g.DrawImage(image, imageRect);
        }

        private void toolStripMenuItem4_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Choose a Specular Image";
            openFileDialog1.Filter = "PNG(.png)|*.png";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                scene.Painting.ActiveLayer.SpecularImage = new BitmapUnsafe(openFileDialog1.FileName);
            }
            mainForm.Refresh();
        }

        #endregion

        #region Height Panel

        private void heightPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Bitmap image = scene.Painting.ActiveLayer.HeightImage.Bitmap;
            Point centerTopRight = new Point(
                (int)((g.VisibleClipBounds.Width - image.Width) / 2),
                (int)((g.VisibleClipBounds.Height - image.Height) / 2));
            Rectangle imageRect = new Rectangle(centerTopRight, image.Size);

            g.FillRectangle(new SolidBrush(Color.LightGray), g.ClipBounds);
            g.DrawImage(image, imageRect);
        }

        private void toolStripMenuItem6_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Choose a Height Image";
            openFileDialog1.Filter = "PNG(.png)|*.png";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                scene.Painting.ActiveLayer.HeightImage = new BitmapUnsafe(openFileDialog1.FileName);
            }
            mainForm.Refresh();
        }

        #endregion

        #region Segments Panel

        private void segmentsPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Bitmap image;
            if (segmentViewSelectToolStripComboBox1.SelectedIndex == 0)
            {
                image = scene.Painting.ActiveLayer.SegmentsImage.Bitmap;
            }
            else
            {
                image = scene.Painting.ActiveLayer.ConnectedComponentsImage;
            }
            Point centerTopRight = new Point(
                (int)((g.VisibleClipBounds.Width - image.Width) / 2),
                (int)((g.VisibleClipBounds.Height - image.Height) / 2));
            Rectangle imageRect = new Rectangle(centerTopRight, image.Size);

            g.FillRectangle(new SolidBrush(Color.LightGray), g.ClipBounds);
            g.DrawImage(image, imageRect);
        }

        private void segmentToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            scene.Renderer.Painter.SegmentImage();
            segmentsPanel.Invalidate();
        }

        private void toolStripComboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            segmentsPanel.Invalidate();
        }

        private void openSegmentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Choose a Segment/Edges Source Image";
            openFileDialog1.Filter = "PNG(.png)|*.png";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                // dispose of the old images
                scene.Painting.ActiveLayer.SegmentsImage.Dispose();
                
                // open the image
                scene.Painting.ActiveLayer.SegmentsImage = new BitmapUnsafe(openFileDialog1.FileName);

                // segment the image
                scene.Renderer.Painter.SegmentImage();

                // generate the edges image
                scene.Renderer.Painter.CompositeEdges();

                //scene.Painting.ActiveLayer.VectorDisplayImage.Save("C:\\Users\\bjonessoda\\Documents\\Class\\CS 499\\PBR2\\PBR\\PBR\\data\\test.png");
            }
            mainForm.Refresh();
        }

        #endregion
        
        #region Vector Panel

        private void vectorPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            Bitmap overlayImage = scene.Painting.ActiveLayer.OverlayImage;
            Bitmap colorImage = scene.Painting.ActiveLayer.ColorImage.Bitmap;
            KDNodeData selectedVector = scene.Painting.ActiveLayer.SelectedVector;
            Rectangle fullImageRect = new Rectangle(new Point(0, 0), overlayImage.Size);

            int row, col;
            float x = 0.0f;
            float y = 0.0f;
            float halfInnerCircleRadius = innerCircleRadius / 2.0f;
            float halfOuterCircleRadius = outerCircleRadius / 2.0f;
            Pen pen;
            double angle;

            g.DrawImage(scene.Painting.ActiveLayer.VectorImage.Bitmap, fullImageRect);
                
            // Iterate through all image segments
            foreach (Segment seg in scene.Painting.ActiveLayer.segments)
            {
                foreach (KDNodeData currNode in seg.vectorsList)
                {
                    if (fullImageRect.Contains(currNode.point))
                    {
                        angle = currNode.angle / 255.0 * Math.PI;
                        row = currNode.point.X - fullImageRect.X;
                        col = currNode.point.Y - fullImageRect.Y;

                        x = (float)(Math.Cos(angle) * VECTORLENGTH);
                        y = (float)(Math.Sin(angle) * VECTORLENGTH) * -1.0f;

                        if (selectedVector == currNode)
                        {
                            pen = selectedPen;
                        }
                        else
                        {
                            pen = unselectedPen;
                        }

                        g.DrawLine(pen, row - x, col - y, row + x, col + y);
                        g.DrawEllipse(pen, row - outerCircleRadius/2, col - outerCircleRadius/2, outerCircleRadius, outerCircleRadius);
                        //g.DrawEllipse(pen, row + x - outerCircleRadius, col + y - outerCircleRadius, outerCircleRadius * 2, outerCircleRadius * 2);
                        //g.DrawEllipse(pen, row - x - outerCircleRadius, col - y - outerCircleRadius, outerCircleRadius * 2, outerCircleRadius * 2);
                    }
                }
            }
        }

        private void vectorPanel_MouseDown(object sender, MouseEventArgs e)
        {
            //vectorPanel.Focus();

            if (e.Button == MouseButtons.Left)
            {
                if (vectorTool == VectorTool.Add)
                {
                    mouseDownPt = new Point(e.X, e.Y);

                    // Determine the current segment
                    int segIndex = scene.Painting.ActiveLayer.ObjectLabels[mouseDownPt.X, mouseDownPt.Y];
                    if (segIndex < 0) // not in a segment
                    {
                        return;
                    }
                    Segment seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];

                    KDNodeData newKDNode = new KDNodeData(mouseDownPt, 0.0, 1.0);
                    seg.vectorsList.Add(newKDNode);
                    scene.Painting.ActiveLayer.SelectedVector = newKDNode;

                    isDraggingAdd = true;

                    vectorPanel.Invalidate(new Rectangle(new Point(e.X, e.Y), new Size(20, 20)));
                }
                else if (vectorTool == VectorTool.Select)
                {
                    double[] dLoc = new double[2] { e.X, e.Y };

                    // Determine the current segment
                    int segIndex = scene.Painting.ActiveLayer.ObjectLabels[e.X, e.Y];
                    if (segIndex < 0)
                    {
                        return;
                    }
                    Segment seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];
                    if (seg.vectorsKDTree.Count == 0)
                    {
                        return;
                    }

                    KDNodeData newSelection = (KDNodeData)seg.vectorsKDTree.Nearest(dLoc);
                    scene.Painting.ActiveLayer.SelectedVector = newSelection;

                    vectorPanel.Invalidate();
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                double[] dLoc = new double[2] { e.X, e.Y };

                // Determine the current segment
                int segIndex = scene.Painting.ActiveLayer.ObjectLabels[e.X, e.Y];
                if (segIndex < 0)
                {
                    return;
                }
                Segment seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];
                scene.SelectedMat = seg.Material;
                PBR.Controls.SegmentPropertiesControl segmentPropertiesControl = (PBR.Controls.SegmentPropertiesControl)mainForm.propertiesTab.Controls[0];
                segmentPropertiesControl.InitSegment();
                mainForm.materialProperties.InitSegment();
                segmentPropertiesControl.Invalidate();
            }
            else if (e.Button == MouseButtons.Middle)
            {
                mouseDownPt = new Point(e.X, e.Y);
                isDraggingMove = true;
            }
        }

        private void vectorPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (vectorTool == VectorTool.Add && isDraggingAdd)
            {
                KDNodeData selectedVector = scene.Painting.ActiveLayer.SelectedVector;

                // angle = [-.5, .5]
                double angle = Math.Atan2(mouseDownPt.Y - e.Y,
                    e.X - mouseDownPt.X) / (2 * Math.PI);
                // angle = [0, 1]
                if (angle < 0.0)
                {
                    angle += 1.0;
                }
                if (angle >= 0.5)
                {
                    angle -= 0.5;
                }
                // angle = [0, 255];
                angle *= 255 * 2;
                selectedVector.angle = angle;

                selectedVector.magnitude = Math.Sqrt(
                        (e.X - mouseDownPt.X) * (e.X - mouseDownPt.X) +
                        (e.Y - mouseDownPt.Y) * (e.Y - mouseDownPt.Y));

                Point clearPoint = new Point(
                        (int)((float)mouseDownPt.X - VECTORLENGTH * 1.0f),
                        (int)((float)mouseDownPt.Y - VECTORLENGTH * 1.0f));
                Size size = new Size((int)(VECTORLENGTH * 2.0), (int)(VECTORLENGTH * 2.0));

                Rectangle invalidateRegion = new Rectangle(clearPoint, size);
                vectorPanel.Invalidate(invalidateRegion);
            }
            else if (isDraggingMove)
            {

            }
        }

        private void vectorPanel_MouseUp(object sender, MouseEventArgs e)
        {
            if (vectorTool == VectorTool.Add && isDraggingAdd)
            {
                isDraggingAdd = false;

                KDNodeData selectedVector = scene.Painting.ActiveLayer.SelectedVector;

                // angle = [-.5, .5]
                double angle = Math.Atan2(mouseDownPt.Y - e.Y, e.X - mouseDownPt.X) / (2 * Math.PI);
                // angle = [0, 1]
                if (angle < 0.0)
                {
                    angle += 1.0;
                }
                // angle [0, 0.5]
                if (angle >= 0.5)
                {
                    angle -= 0.5;
                }
                // angle = [0, 255];
                angle *= 255 * 2;

                double[] dLoc = new double[2] { (double)mouseDownPt.X, (double)mouseDownPt.Y };
                selectedVector.point = new Point(mouseDownPt.X, mouseDownPt.Y);
                selectedVector.angle = angle;

                // Determine the current segment
                int segIndex = scene.Painting.ActiveLayer.ObjectLabels[mouseDownPt.X, mouseDownPt.Y];
                if (segIndex < 0)
                {
                    return;
                }
                Segment seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];

                seg.vectorsKDTree.Insert(dLoc, selectedVector);

                //ComputeVectorField();
                vectorsDirty = true;
                idleTime = 0;

                //vectorPanel.Invalidate(seg.rect);
            }
            else if (isDraggingMove)
            {
                isDraggingMove = false;

                int newVertScroll = vectorBkgdPanel.VerticalScroll.Value - e.Y + mouseDownPt.Y;
                int newHorizScroll = vectorBkgdPanel.HorizontalScroll.Value - e.X + mouseDownPt.X;

                if(newVertScroll > vectorBkgdPanel.VerticalScroll.Maximum)
                    newVertScroll = vectorBkgdPanel.VerticalScroll.Maximum;
                if(newVertScroll < vectorBkgdPanel.VerticalScroll.Minimum)
                    newVertScroll = vectorBkgdPanel.VerticalScroll.Minimum;
                if(newHorizScroll > vectorBkgdPanel.HorizontalScroll.Maximum)
                    newHorizScroll = vectorBkgdPanel.HorizontalScroll.Maximum;
                if(newHorizScroll < vectorBkgdPanel.HorizontalScroll.Minimum)
                    newHorizScroll = vectorBkgdPanel.HorizontalScroll.Minimum;

                vectorBkgdPanel.VerticalScroll.Value = newVertScroll;
                vectorBkgdPanel.HorizontalScroll.Value = newHorizScroll;
            }
        }

        /// <summary>
        /// Create the image with the current vector field
        /// </summary>
        public unsafe void ComputeVectorField()
        {
            Bitmap vectorImage = scene.Painting.ActiveLayer.VectorImage.Bitmap;

            Rectangle fullImageRect = new Rectangle(new Point(0, 0), vectorImage.Size);
            AForge.Imaging.Filters.CanvasFill canvasFill = new AForge.Imaging.Filters.CanvasFill(fullImageRect, Color.FromArgb(0, 0, 0, 0));
            canvasFill.ApplyInPlace(vectorImage);

            // We draw to VectorImage
            Graphics g = Graphics.FromImage(vectorImage);

            // Initialize variables
            Bitmap overlayImage = scene.Painting.ActiveLayer.OverlayImage;
            Bitmap colorImage = scene.Painting.ActiveLayer.ColorImage.Bitmap;
            RadialBasisInterp radialBasisInterp = new RadialBasisInterp();

            float x = 0.0f;
            float y = 0.0f;
            int row, col;
            double angle;

            // Draw the source Image and the overlay (the edges)
            g.DrawImage(colorImage, fullImageRect);
            g.DrawImage(overlayImage, fullImageRect);

            // For every 'distance' in the image
            for (col = 0; col < fullImageRect.Height; col += distance)
            {
                for (row = 0; row < fullImageRect.Width; row += distance)
                {
                    // Interpolate angle
                    int segIndex = scene.Painting.ActiveLayer.ObjectLabels[row, col];
                    if (segIndex < 0)
                    {
                        continue;
                    }
                    Segment seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];
                    if (seg.vectorsKDTree.Count == 0)
                    {
                        angle = 0.0;
                    }
                    else
                    {
                        angle = radialBasisInterp.InterpAngle(seg.vectorsKDTree, new Point(row, col));
                    }

                    // Scale the angle
                    angle = angle / 255.0 * Math.PI;
                    
                    // Determine the line endpoints (y is neg b/c convert
                    // coord-spaces
                    x = (float)(Math.Cos(angle) * lineLength);
                    y = (float)(Math.Sin(angle) * lineLength) * -1.0f;
                    g.DrawLine(vectorPen, row - x, col - y, row + x, col + y);
                }
            }

            g.Flush();
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
        }

        private void addVectorToolstripBtn_Click(object sender, EventArgs e)
        {
            vectorPanel.Cursor = Cursors.Cross;
            vectorTool = VectorTool.Add;
            addVectorToolstripBtn.Checked = !addVectorToolstripBtn.Checked;
        }

        private void selectVectorToolstripBtn_Click(object sender, EventArgs e)
        {
            vectorPanel.Cursor = Cursors.Default;
            vectorTool = VectorTool.Select;
            selectVectorToolstripBtn.Checked = !selectVectorToolstripBtn.Checked;
        }

        private void ClearToolStripButton_Click(object sender, EventArgs e)
        {
            scene.Painting.ActiveLayer.SelectedVector = null;
            foreach(Segment seg in scene.Painting.ActiveLayer.segments)
            {
                seg.vectorsKDTree = new KDTreeDLL.KDTree(2);
                seg.vectorsList.Clear();
            }
            
            ComputeVectorField();
            vectorPanel.Invalidate();
        }
        
        private void vectorPanel_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            switch(e.KeyCode)
            {
                case Keys.Delete:
                    KDNodeData selectedVector = scene.Painting.ActiveLayer.SelectedVector;
                    if(selectedVector != null)
                    {
                        Double[] dLoc = new Double[2] {selectedVector.point.X, selectedVector.point.Y};
                        
                        // Determine the current segment
                        int segIndex = scene.Painting.ActiveLayer.ObjectLabels[selectedVector.point.X, selectedVector.point.Y];
                        if (segIndex < 0)
                        {
                            return;
                        }
                        Segment seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];
                        seg.vectorsKDTree.Delete(dLoc);
                        seg.vectorsList.Remove(selectedVector);
                    }
                    ComputeVectorField();
                    vectorPanel.Invalidate();
                    break;
                case Keys.Add:
                    scene.Zoom += 0.25f;
                    vectorPanel.Invalidate();
                    break;
                case Keys.Subtract:
                    scene.Zoom -= 0.25f;
                    vectorPanel.Invalidate();
                    break;
                case Keys.A:
                    vectorPanel.Cursor = Cursors.Cross;
                    vectorTool = VectorTool.Add;
                    addVectorToolstripBtn.Checked = !addVectorToolstripBtn.Checked;
                    break;
                case Keys.S:
                    vectorPanel.Cursor = Cursors.Default;
                    vectorTool = VectorTool.Select;
                    selectVectorToolstripBtn.Checked = !selectVectorToolstripBtn.Checked;
                    break;
                default:
                    break;
            }
        }

        private void vectorPanel_DragDrop(object sender, DragEventArgs e)
        {
            Point mousePt = vectorPanel.PointToClient(MousePosition);
            int segIndex = scene.Painting.ActiveLayer.ObjectLabels[mousePt.X, mousePt.Y];
            if (segIndex < 0) // not in a segment
            {
                return;
            }
            Segment seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];
            String matName = (String)e.Data.GetData(DataFormats.Text);

            foreach (Material mat in scene.Painting.MaterialList)
            {
                if (mat.Name == matName)
                {
                    seg.Material = mat;
                }
            }
            scene.SelectedMat = seg.Material;

            int i = 0;
            i++;

            PBR.Controls.SegmentPropertiesControl segmentPropertiesControl = (PBR.Controls.SegmentPropertiesControl)mainForm.propertiesTab.Controls[0];
            segmentPropertiesControl.InitSegment();
            mainForm.materialProperties.InitSegment();
            segmentPropertiesControl.Invalidate();
        }

        private void vectorPanel_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Copy;
        }

        private void vectorPanel_DragLeave(object sender, EventArgs e)
        {
            
        }

        #endregion

        #region Edges Tab

        private void edgesPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Bitmap image = scene.Painting.ActiveLayer.EdgesImage.Bitmap;
            Point centerTopRight = new Point(
                (int)((g.VisibleClipBounds.Width - image.Width) / 2),
                (int)((g.VisibleClipBounds.Height - image.Height) / 2));
            Rectangle imageRect = new Rectangle(centerTopRight, image.Size);

            g.FillRectangle(new SolidBrush(Color.LightGray), g.ClipBounds);
            g.DrawImage(image, imageRect);
        }

        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            scene.Renderer.Painter.CompositeEdges();
            edgesPanel.Invalidate();
        }

        private void saveToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            saveFileDialog1.Title = "Save Edges Image";
            saveFileDialog1.DefaultExt = ".png";
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                scene.Painting.ActiveLayer.EdgesImage.Bitmap.Save(saveFileDialog1.FileName);
            }
        }

        private void openToolStripMenuItem2_Click(object sender, EventArgs e)
        {
            openFileDialog1.Title = "Choose an Edges Image";
            openFileDialog1.Filter = "PNG(.png)|*.png";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                scene.Painting.ActiveLayer.EdgesImage = new BitmapUnsafe(openFileDialog1.FileName);
                AForge.Imaging.Filters.GrayscaleBT709 grayscaleFilter = new AForge.Imaging.Filters.GrayscaleBT709();
                scene.Painting.ActiveLayer.EdgesImage.Bitmap = grayscaleFilter.Apply(scene.Painting.ActiveLayer.EdgesImage.Bitmap);
            }
            mainForm.Refresh();
        }

        private void segmentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            scene.Renderer.Painter.SegmentImage();
        }



        #endregion

        #region Paint Panel

        private void paintPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Bitmap image = scene.Painting.ActiveLayer.PaintImage.Bitmap;
            Point centerTopRight = new Point(
                (int)((g.VisibleClipBounds.Width - image.Width) / 2),
                (int)((g.VisibleClipBounds.Height - image.Height) / 2));
            Rectangle imageRect = new Rectangle(centerTopRight, image.Size);

            g.FillRectangle(new SolidBrush(Color.LightGray), g.ClipBounds);
            g.DrawImage(image, imageRect);
            if (showBorders)
            {
                g.DrawImage(scene.Painting.ActiveLayer.OverlayImage, imageRect);
            }
        }

        private void GeneratePainting()
        {
            // Clear image
            Rectangle fullImage = new Rectangle(new Point(0,0), scene.Painting.ActiveLayer.PaintImage.Bitmap.Size);
            AForge.Imaging.Filters.CanvasFill canvasFill = new AForge.Imaging.Filters.CanvasFill(fullImage, Color.FromArgb(0, 0, 0, 0));
            canvasFill.ApplyInPlace(scene.Painting.ActiveLayer.PaintImage.Bitmap);

            //Graphics gColor = Graphics.FromImage(scene.Painting.ActiveLayer.PaintImage.Bitmap);
            //Graphics gHeight = Graphics.FromImage(scene.Painting.ActiveLayer.HeightImage.Bitmap);
            scene.Painting.ActiveLayer.PaintImage.Dispose();
            scene.Painting.ActiveLayer.PaintImage = new BitmapUnsafe("PaintImage", scene.Painting.ActiveLayer.ImageTier.ColorImages[3]);

            if (scene.Painting.IsRandom)
            {
                scene.Renderer.Painter.PaintRandomNew(scene.Painting.ActiveLayer.PaintImage.Bitmap, scene.Painting.ActiveLayer.HeightImage.Bitmap,
                    fullImage, true);
            }
            else
            {
                scene.Renderer.Painter.PaintSaved(scene.Painting.ActiveLayer.PaintImage.Bitmap, scene.Painting.ActiveLayer.HeightImage.Bitmap,
                    fullImage, true);
            }
            // Lock the painting
            scene.Painting.IsRandom = false;
            lockToolStripButton.Checked = true;

            // clear the window
            paintPanel.Invalidate();
        }

        private void paintTab_Enter(object sender, EventArgs e)
        {
            //mainForm.PropertiesBindingSource.DataSource = scene.Renderer.Painter;
        }

        private void generatePainting_Click(object sender, EventArgs e)
        {
            //Thread paintThread = new Thread(new ThreadStart(GeneratePainting));
            //paintThread.Start();
            GeneratePainting();
        }

        private void savePainting_Click(object sender, EventArgs e)
        {
            saveFileDialog1.Title = "Save Paint Image";
            saveFileDialog1.DefaultExt = ".png";
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                scene.Painting.ActiveLayer.PaintImage.Bitmap.Save(saveFileDialog1.FileName);
            }
        }

        private void showBorders_Click(object sender, EventArgs e)
        {
            showBordersBtn.Checked = !showBordersBtn.Checked;
            showBorders = showBordersBtn.Checked;
            paintPanel.Invalidate();
        }

        private void paintPanel_MouseDown(object sender, MouseEventArgs e)
        {
            paintPanel.Focus();

            // Determine the current segment
            int segIndex = scene.Painting.ActiveLayer.ObjectLabels[e.X, e.Y];
            if (segIndex < 0) // not in a segment
            {
                return;
            }
            Segment seg = (Segment)scene.Painting.ActiveLayer.segments[segIndex];
            //mainForm.PropertiesBindingSource.DataSource = seg;
            scene.SelectedMat = seg.Material;
            PBR.Controls.SegmentPropertiesControl segmentPropertiesControl = (PBR.Controls.SegmentPropertiesControl)mainForm.segmentPropertiesControl;
            segmentPropertiesControl.InitSegment();
            PBR.Controls.StrokeProperties strokeProperties = (PBR.Controls.StrokeProperties)mainForm.strokeProperties;
            strokeProperties.InitStroke();
            strokeProperties.Invalidate();
        }

        private void paintTab_MouseDown(object sender, MouseEventArgs e)
        {
            Point hitPoint = new Point(e.Y, e.Y);
            if (!paintPanel.DisplayRectangle.Contains(hitPoint) && !paintToolStrip.DisplayRectangle.Contains(hitPoint))
            {
                paintTab.Focus();
                mainForm.PropertiesBindingSource.DataSource = scene.Renderer.Painter;
            }
        }

        private void lockToolStripButton_Click(object sender, EventArgs e)
        {
            scene.Painting.IsRandom = !(lockToolStripButton.Checked);
        }

        #endregion

        #region Render Panel

        private void renderPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Bitmap image = scene.Painting.OutputImage.Bitmap;
            Point centerTopRight = new Point(
                (int)((g.VisibleClipBounds.Width - image.Width) / 2),
                (int)((g.VisibleClipBounds.Height - image.Height) / 2));
            Rectangle imageRect = new Rectangle(centerTopRight, image.Size);

            g.FillRectangle(new SolidBrush(Color.LightGray), g.ClipBounds);
            g.DrawImage(image, imageRect);
        }

        private void renderToolStripButton_Click(object sender, EventArgs e)
        {
            scene.Renderer.Execute();
            renderPanel.Invalidate();
        }

        private void renderPanel_Enter(object sender, EventArgs e)
        {
            mainForm.PropertiesBindingSource.DataSource = scene.SelectedMat;
        }

        private void renderPanel_Click(object sender, EventArgs e)
        {
            mainForm.PropertiesBindingSource.DataSource = scene.SelectedMat;
        }

        #endregion

        #region Tweak Panel

        private void tweakPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Bitmap image = scene.Painting.ActiveLayer.PaintImage.Bitmap;
            Point centerTopRight = new Point(
                (int)((g.VisibleClipBounds.Width - image.Width) / 2),
                (int)((g.VisibleClipBounds.Height - image.Height) / 2));
            Rectangle imageRect = new Rectangle(centerTopRight, image.Size);

            g.FillRectangle(new SolidBrush(Color.LightGray), g.ClipBounds);
            g.DrawImage(image, imageRect);
        }

        private void tweakPanel_MouseDown(object sender, MouseEventArgs e)
        {
        }

        private void tweakPanel_MouseMove(object sender, MouseEventArgs e)
        {

        }

        private void tweakPanel_MouseUp(object sender, MouseEventArgs e)
        {

        }

        #endregion

        #region Properties



        #endregion

        #region Events

        private void handle_zoom(object sender, EventArgs e)
        {

        }

        #endregion

        private void mainTabControl_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Updates the sizes of all the panels after loading an image
        /// </summary>
        public void UpdateSizes()
        {
            Size newSize = scene.Painting.ActiveLayer.ColorImage.Bitmap.Size;
            colorPanel.Size = newSize;
            edgesPanel.Size = newSize;
            segmentsPanel.Size = newSize;
            vectorPanel.Size = newSize;
            renderPanel.Size = newSize;
            paintPanel.Size = newSize;
        }

        private void panel7_Paint(object sender, PaintEventArgs e)
        {

        }

        private void segmentPanel_Paint(object sender, PaintEventArgs e)
        {

        }

        private void idleTimer_Tick(object sender, EventArgs e)
        {
            if (vectorsDirty)
            {
                idleTime += idleTimer.Interval;
                if (idleTime > IDLETIMEOUT)
                {
                    ComputeVectorField();
                    vectorPanel.Invalidate();
                    vectorsDirty = false;
                }
            }
        }

        private void GlobalPropertiesToolStripButton_Click(object sender, EventArgs e)
        {

        }

        private void vectorPanel_MouseEnter(object sender, EventArgs e)
        {
            //vectorPanel.Focus();
        }

        private void vectorBkgdPanel_MouseEnter(object sender, EventArgs e)
        {
            //vectorBkgdPanel.Focus();
        }


 



    }
}
